package br.ufal.tci.nexos.arcolive.tests;

import java.io.File;
import java.io.IOException;
import javax.media.ConfigureCompleteEvent;
import javax.media.ControllerEvent;
import javax.media.ControllerListener;
import javax.media.DataSink;
import javax.media.EndOfMediaEvent;
import javax.media.Format;
import javax.media.Manager;
import javax.media.MediaLocator;
import javax.media.PrefetchCompleteEvent;
import javax.media.Processor;
import javax.media.ProcessorModel;
import javax.media.RealizeCompleteEvent;
import javax.media.ResourceUnavailableEvent;
import javax.media.control.TrackControl;
import javax.media.datasink.DataSinkErrorEvent;
import javax.media.datasink.DataSinkEvent;
import javax.media.datasink.DataSinkListener;
import javax.media.datasink.EndOfStreamEvent;
import javax.media.format.VideoFormat;
import javax.media.protocol.ContentDescriptor;
import javax.media.protocol.DataSource;
import javax.media.protocol.FileTypeDescriptor;

import jmapps.util.StateHelper;
import br.ufal.tci.nexos.arcolive.beans.ImageDataSource;


public class ScreenGrabberPrototype implements ControllerListener, DataSinkListener {

	private boolean doIt(
	        int width,
	        int height,
	        int frameRate,
	        MediaLocator outML)
	    {
	        ImageDataSource ids = new ImageDataSource(width, height, frameRate);
	 
	        Processor p;
	 
	                
	        try
	        {
	            System.err.println(
	                "- create processor for the image datasource ...");
	            p = Manager.createProcessor(ids);
	        }
	        catch (Exception e)
	        {
	            System.err.println(
	                "Yikes! Cannot create a processor from the data source.");
	            return false;
	        }
	 
	        p.addControllerListener(this);
	 
	        // Put the Processor into configured state so we can set
	        // some processing options on the processor.
	        p.configure();
	        if (!waitForState(p, Processor.Configured))
	        {
	            System.err.println("Failed to configure the processor.");
	            return false;
	        }
	 
	        // Set the output content descriptor to QuickTime.
	        
	        p.setContentDescriptor(new ContentDescriptor(FileTypeDescriptor.MSVIDEO));
	 
	        // Query for the processor for supported formats.
	        // Then set it on the processor.
//	        TrackControl tcs[] = p.getTrackControls();
//	        Format f[] = tcs[0].getSupportedFormats();
//	        if (f == null || f.length <= 0)
//	        {
//	            System.err.println(
//	                "The mux does not support the input format: "
//	                    + tcs[0].getFormat());
//	            return false;
//	        }
//	 
//	        tcs[0].setFormat(f[0]);
//	 
//	        System.err.println("Setting the track format to: " + f[0]);
//	 
	        // We are done with programming the processor. Let's just
	        // realize it.
	        p.realize();
	        if (!waitForState(p, Processor.Realized))
	        {
	            System.err.println("Failed to realize the processor.");
	            return false;
	        }
	 
	        // Now, we'll need to create a DataSink.
	        DataSink dsink;
	        if ((dsink = createDataSink(p, outML)) == null)
	        {
	            System.err.println(
	                "Failed to create a DataSink for the given output MediaLocator: "
	                    + outML);
	            return false;
	        }
	 
//	        dsink.addDataSinkListener(this);
//	        MediaRecorder mr = new MediaRecorder();
//	        mr.setDatasource(p.getDataOutput());
//	        mr.setTarget(outML);
//	        try {
//				mr.record(5000);
//			} catch (CannotRecordMediaException e1) {
//				// TODO Auto-generated catch block
//				e1.printStackTrace();
//			}
	        fileDone = false;
	 
	        System.err.println("start processing...");
	 
	        // OK, we can now start the actual transcoding.
	        try
	        {
	            p.start();
	            dsink.start();
	        }
	        catch (IOException e)
	        {
	            System.err.println("IO error during processing");
	            return false;
	        }
	 
	        StateHelper sh = new StateHelper(p);
	        sh.playToEndOfMedia(10000);
	        sh.close();
	        
	        // Wait for EndOfStream event.
	        //waitForFileDone();
	 
//	        try {
//				mr.closeRecorder();
//			} catch (CannotStopRecorderException e) {
//				// TODO Auto-generated catch block
//				e.printStackTrace();
//			}
	        
	        // Cleanup.
	        try
	        {
	            dsink.close();
	        }
	        catch (Exception e)
	        {
	        }
	        p.removeControllerListener(this);
	 
	        System.err.println("...done processing.");
	 
	        return true;
	    }
	 
	    /**
	    * Create the DataSink.
	    */
	    private DataSink createDataSink(Processor p, MediaLocator outML)
	    {
	 
	        DataSource ds;
	 
	        if ((ds = p.getDataOutput()) == null)
	        {
	            System.err.println(
	                "Something is really wrong: the processor does not have an output DataSource");
	            return null;
	        }
	 
	        DataSink dsink;
	 
	        try
	        {
	            System.err.println("- create DataSink for: " + outML);
	            dsink = Manager.createDataSink(ds, outML);
	            dsink.open();
	        }
	        catch (Exception e)
	        {
	            System.err.println("Cannot create the DataSink: " + e);
	            return null;
	        }
	 
	        return dsink;
	    }
	 
	    private Object waitSync = new Object();
	    private boolean stateTransitionOK = true;
	 
	    /**
	    * Block until the processor has transitioned to the given state.
	    * Return false if the transition failed.
	    */
	    private boolean waitForState(Processor p, int state)
	    {
	        synchronized (waitSync)
	        {
	            try
	            {
	                while (p.getState() < state && stateTransitionOK)
	                    waitSync.wait();
	            }
	            catch (Exception e)
	            {
	            }
	        }
	        return stateTransitionOK;
	    }
	 
	    /**
	    * Controller Listener.
	    */
	    public void controllerUpdate(ControllerEvent evt)
	    {
	 
	        if (evt instanceof ConfigureCompleteEvent
	            || evt instanceof RealizeCompleteEvent
	            || evt instanceof PrefetchCompleteEvent)
	        {
	            synchronized (waitSync)
	            {
	                stateTransitionOK = true;
	                waitSync.notifyAll();
	            }
	        }
	        else if (evt instanceof ResourceUnavailableEvent)
	        {
	            synchronized (waitSync)
	            {
	                stateTransitionOK = false;
	                waitSync.notifyAll();
	            }
	        }
	        else if (evt instanceof EndOfMediaEvent)
	        {
	            evt.getSourceController().stop();
	            evt.getSourceController().close();
	        }
	    }
	 
	    private Object waitFileSync = new Object();
	    private boolean fileDone = false;
	    private boolean fileSuccess = true;
	 
	    /**
	    * Block until file writing is done.
	    */
	    private boolean waitForFileDone()
	    {
	        synchronized (waitFileSync)
	        {
	            try
	            {
	                while (!fileDone)
	                    waitFileSync.wait();
	            }
	            catch (Exception e)
	            {
	            }
	        }
	        return fileSuccess;
	    }
	 
	    /**
	    * Event handler for the file writer.
	    */
	    public void dataSinkUpdate(DataSinkEvent evt)
	    {
	 
	        if (evt instanceof EndOfStreamEvent)
	        {
	            synchronized (waitFileSync)
	            {
	                fileDone = true;
	                waitFileSync.notifyAll();
	            }
	        }
	        else if (evt instanceof DataSinkErrorEvent)
	        {
	            synchronized (waitFileSync)
	            {
	                fileDone = true;
	                fileSuccess = false;
	                waitFileSync.notifyAll();
	            }
	        }
	    }
	 
	    public static void main(String args[]) throws Exception
	    {
	        //jpegCreator.main(null);
	        //if (args.length == 0)
	        // prUsage();
	 
	        // Parse the arguments.
	        int i = 0;
	        int width = -1, height = -1, frameRate = 1000;
	        String outputURL = null;
	 
	        width = 1024;
	        height = 768;
	        outputURL = "test.avi";
	 
	        // Generate the output media locators.
	        MediaLocator oml;
	 
	        if ((oml = createMediaLocator(outputURL)) == null)
	        {
	            System.err.println("Cannot build media locator from: " + outputURL);
	            System.exit(0);
	        }
	 
	        ScreenGrabberPrototype imageToMovie = new ScreenGrabberPrototype();
	        imageToMovie.doIt(width, height, frameRate, oml);
	 
	        System.exit(0);
	    }
	
	    private static MediaLocator createMediaLocator(String url)
	    {
	 
	        MediaLocator ml;
	 
	        if (url.indexOf(":") > 0 && (ml = new MediaLocator(url)) != null)
	            return ml;
	 
	        if (url.startsWith(File.separator))
	        {
	            if ((ml = new MediaLocator("file:" + url)) != null)
	                return ml;
	        }
	        else
	        {
	            String file =
	                "file:" + System.getProperty("user.dir") + File.separator + url;
	            if ((ml = new MediaLocator(file)) != null)
	                return ml;
	        }
	 
	        return null;
	    }
}